(Rust) Zeroize memory
tl;dr
Use zeroize crate to zeroize sensitive data allocated in heep.
seed, secret key, mnemonic, password, randomness
Why zeroizing memory is needed
Side-Channel Security
Forward Secrecy
Rustの言語仕様(Drop trait)でaccidentalにstackやheapに残っている値が読み取られるのは防がれている。しかし、外部プログラム(例えば、デバッガーや外部物理マシン)がそれらのleftover valuesを読み取ることができる可能性がある。
OpenSSLのHeartbleed Bugはまさにこれ。
https://www.symantec.com/content/ja/jp/enterprise/images/outbreak/Heartbleed_vulnerability.pdf
コンパイラの最適化によりclearning codeが省略されたりreorderedされる可能性がある。(Memory leaks are considered memory safe)
メモリ安全とは、メモリの範囲外アクセスや二重解放、ヌル参照、未初期化領域へのアクセスがない状態。 ただし、Rust の言うメモリ安全とは、メモリリークをしないことを保証するものではない。
e.x. Rc/Arc, mem::forget, Box::into_raw
https://doc.rust-jp.rs/book/second-edition/ch15-06-reference-cycles.html
https://gyazo.com/8563847d06f5e44876413283200a5dfd
https://anssi-fr.github.io/rust-guide/04_language.html#memory-management
How to zero a buffer
http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html
remote code executionやremotelyにuninitializedなmemoryをreadする脆弱性を防ぐ
OpenSSLのHeartbleed Bugなど
http://heartbleed.com/
Heartbleed bug による秘密鍵漏洩の現実性について
https://sect.iij.ad.jp/d/2014/04/159520.html
OpenSSLの脆弱性(CVE-2014-0160)関連の情報をまとめてみた
https://piyolog.hatenadiary.jp/entry/20140410/1397139257
Preventing compiler optimizations
コンパイラの最適化により、
dataのclearningを省略する可能性
呼び出された関数をインライン化しclearing codeをstackする可能性
Windows: SecureZeroMemory
Linux: memzero_explicit
More great references
https://github.com/dalek-cryptography/curve25519-dalek/issues/11
https://github.com/rust-lang/rfcs/issues/2533
https://github.com/briansmith/ring/issues/566
https://internals.rust-lang.org/t/volatile-and-sensitive-memory/3188
https://github.com/rust-lang/rfcs/pull/320
https://github.com/rust-lang/rfcs/issues/1853
https://www.youtube.com/watch?v=cQ9wTyYCdNU
http://bristolcrypto.blogspot.com/2017/01/rwc-2017-erasing-secrets-from-ram.html
https://internals.rust-lang.org/t/volatile-and-sensitive-memory/3188
https://tomo-wait-for-it-yuki.hatenablog.com/entry/2019/02/16/063317
Libraries
Securely zero memory while avoiding compiler optimizations.
https://github.com/iqlusioninc/crates/tree/develop/zeroize
https://github.com/iqlusioninc/crates/tree/develop/zeroize_derive
Helpers for clearing sensitive data on the stack and heap
https://github.com/cesarb/clear_on_drop
Replace ClearOnDrop with Zeroize
https://github.com/lovesh/amcl_rust_wrapper/issues/2
code:1.rs
#inline(always)
fn zeroize_hack<Z: Default>(z: &mut Z) {
use core::{ptr, sync::atomic};
unsafe { ptr::write_volatile(z, Z::default()); }
atomic::compiler_fence(atomic::Ordering::SeqCst);
}
Findings in some audit reports
https://gyazo.com/1770c5f401375d940f18ccd34d41348b
https://github.com/poanetwork/threshold_crypto/issues/24
impl Zeroize for PublicKey
We do not require this, and we do not make PubLicKey be Drop, but someone might want this for something.
https://github.com/w3f/schnorrkel/commit/94cf712f97fcfb48e617d6d0b1ba95590e733c2b
In curv
https://github.com/KZen-networks/curv/search?q=zeroize&unscoped_q=zeroize
https://gyazo.com/da9a35f63951e26f9ce4a6667f1b645c
Zeroize
https://github.com/w3f/schnorrkel/blob/0645b98ff5e8b49a3dccc1146407897afddbffa5/src/keys.rs#L108-L117
code:1.rs
impl Zeroize for MiniSecretKey {
fn zeroize(&mut self) {
super::zeroize_hack(self);
}
}
impl Drop for MiniSecretKey {
fn drop(&mut self) {
self.zeroize();
}
}
https://github.com/w3f/schnorrkel/blob/0724aced214fb39b54552f389aeaa1c9172afd05/src/lib.rs#L225-L230
dalek
implement Zeroize for Scalar and MontgomeryPoint
https://github.com/dalek-cryptography/curve25519-dalek/pull/236
no_std support appears to be broken
https://github.com/cesarb/clear_on_drop/issues/20
Hello Rust 2018 + Zeroize Flag For simple support
https://github.com/dalek-cryptography/curve25519-dalek/pull/258
Grin
Fill BlindingFactor with zeros on Drop
https://github.com/mimblewimble/grin/pull/2847
code:test_for_zeroize.rs
// This tests cleaning of BlindingFactor (e.g. secret key) on Drop.
// To make this test fail, just remove Zeroize derive from BlindingFactor definition.
#test
fn blinding_factor_clear_on_drop() {
// Create buffer for blinding factor filled with non-zero bytes.
let bf_bytes = 0xAA; SECRET_KEY_SIZE;
let ptr = {
// Fill blinding factor with some "sensitive" data
let bf = BlindingFactor::from_slice(&bf_bytes..);
bf.0.as_ptr()
// -- after this line BlindingFactor should be zeroed
};
// Unsafely get data from where BlindingFactor was in memory. Should be all zeros.
let bf_bytes = unsafe { from_raw_parts(ptr, SECRET_KEY_SIZE) };
// There should be all zeroes.
let mut all_zeros = true;
for b in bf_bytes {
if *b != 0x00 {
all_zeros = false;
}
}
assert!(all_zeros)
}
Fill SecretKey with zeros on Drop for security
https://github.com/mimblewimble/rust-secp256k1-zkp/pull/51
Clear on Drop
https://github.com/lovesh/amcl_rust_wrapper/blob/7ed25e32d30cfc4ce46c51d7acc2d096aee98a7a/src/field_elem.rs#L53-L57
code:1.rs
impl Drop for FieldElement {
fn drop(&mut self) {
self.value.w.clear();
}
}
https://github.com/dalek-cryptography/ed25519-dalek/blob/a65683accddec9caacdaea5a0efcae54f6898ec5/src/secret.rs#L51-L56
code:1.rs
/// Overwrite secret key material with null bytes when it goes out of scope.
impl Drop for SecretKey {
fn drop(&mut self) {
self.0.clear();
}
}
std/core crate
std::mem::forget
意図的なメモリリーク。(Memory leaks are memory safe!!)
memory leaks can be caused buy things like reference cycles and thread deadlock.
https://doc.rust-lang.org/std/mem/fn.forget.html
Rustのstd::mem::forgetがunsafeでない理由
https://qnighy.hatenablog.com/entry/2017/04/14/070000
Rustのforget関数
https://qiita.com/takayahilton/items/75be48b07f8d57aed0d0
std::ptr::write_volatile (unsafe)
https://doc.rust-lang.org/std/ptr/fn.write_volatile.html
Performs a volatile write of a memory location with the given value without reading or dropping the old value.
Volatile operations are intended to act on I/O memory, and are guaranteed to not be elided or reordered by the compiler across other volatile operations.
src is moved into the location pointed to by dst
std::atomic::compiler_fence
https://doc.rust-lang.org/std/sync/atomic/fn.compiler_fence.html
与えられたstd::atomic::Orderingにしたがってコンパイラによるre-orderingを防ぐ。
Note that it does not prevent the hardware from doing such re-ordering.
std::atomic::Ordering::SeqCst: no re-ordering of reads and writes across this point is allowed.
std::mem::transmute (unsafe), std::Box::into_raw()
https://doc.rust-lang.org/std/mem/fn.transmute.html
https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw
Rustオブジェクトの削除タイミングを手動で制御する
https://qiita.com/tatsuya6502/items/b9801d92f71e24874c9d
Rustで実行可能なメモリを確保
https://totem3.hatenablog.jp/entry/2018/08/20/214313
memo
the threat is not only leaking secrets from within the process, but also someone from the outside attaching a debugger, the whole process memory being dumped (a core dump, a VM snapshot, suspend-to-disk), or even direct access to the memory hardware (cold boot attack). If all traces of the secret had been safely erased by then, you're safe.
https://github.com/rust-lang/rfcs/issues/1853#issuecomment-275568249
As I understand Laurent Simon's work, zeroing the secret data needed in cryptography can be cheap compared with the cost of doing the cryptography that uses it, but there are two bad situations one must avoid :
If all functions zero their own data, then we zero needlessly anytime a loop repeatedly calls the same functions, as always happens in cryptography.
Any data we zero but never touch results in cache misses, and squandered cache lines, as happens with the clear_on_drop crate's clear_stack_on_return function.
I'll close up this issue now because it takes the wrong approach. We do not want stack types that zero themselves per se. And boxing the secret data ala ClearOnDrop<Box<T>> does not work either unless you can manually tune specific workspaces that way.
Instead, we should store intermediate sensitive data in a stack-like way so that the same memory can easily be reused for different mathematical objects, but we need to zero exactly the used stack space when done with sensitive code, and take care with how else this stack gets used.
For this, we need instrumentation that tracks exactly how much stack these sensitive functions use, statically if possible, but dynamically when necessary, along with a volatile_zero_stack(bytes: usize) function to zero the exact quantity of stack space after use.
https://github.com/briansmith/ring/issues/566#issuecomment-318673260
多くの中間値が生成される、key materialをstackに保持しsecret codeを使ったstackを全てzeroizeする必要。
You do not necessarily want to zero cryptographic key material when they types get dropped, as you might make many intermediate values. You often want to instrument your functions, keep key material only on the stack, and zero all the stack that you used when your cryptographic routines conclude.
https://github.com/mimblewimble/grin/pull/2847#issuecomment-499268857
https://docs.rs/zeroize/0.9.3/zeroize/#stackheap-zeroing-notes
This crate can be used to zero values from either the stack or the heap.
However, be aware several operations in Rust can unintentionally leave copies of data in memory. This includes but is not limited to:
Moves and Copy
Heap reallocation when using Vec and String
Borrowers of a reference making copies of the data
Pin can be leveraged in conjunction with this crate to ensure data kept on the stack isn't moved.
The Zeroize impls for Vec and String zeroize the entire capacity of their backing buffer, but cannot guarantee copies of the data were not previously made by buffer reallocation. It's therefore important when attempting to zeroize such buffers to initialize them to the correct capacity, and take care to prevent subsequent reallocation.
The secrecy crate provides higher-level abstractions for eliminating usage patterns which can cause reallocations:
#Rust